#include "cbardecoration.h"

#include <buola/image/cgc.h>
#include <buola/gui/x11/cpixmap.h>
#include <buola/gui/x11/cfixesregion.h>
#include <buola/gui/x11/cx11display.h>
#include <buola/image/generator/gaussian.h>
#include <buola/gui/cpointer.h>
#include <buola/widgets/ctheme.h>
#include <buola/image/cpattern.h>
#include <buola/algorithm/container.h>

#include "cclient.h"
#include "cmanagedclient.h"

namespace buola { namespace bwm {

bool CBarDecoration::mImageInit;
img::CColor CBarDecoration::mInactiveColor(0.9,0.9,0.9);
//gui::x11::CPixmap *CBarDecoration::mShadow;
//CSize_d CBarDecoration::mShadowSize;
//gui::x11::CPixmap *CBarDecoration::ture;

CBarDecoration::CBarDecoration(CManagedClient *pClient)
    :   CManagedDecoration(pClient)
{
    mManagedClient=pClient;
    mPressed=nullptr;
    mInside=nullptr;
}

CBarDecoration::~CBarDecoration()
{
}

void CBarDecoration::InitInternal()
{
    InitStatic();

    mDL=1;
    mDR=1;
    mDT=25;
    mDB=1;

    mSL=23;
    mSR=23;
    mST=0;
    mSB=23;

    mL=mSL+mDL;
    mR=mSR+mDR;
    mT=mST+mDT;
    mB=mSB+mDB;

    construct_back(mButtons,BUTTON_CLOSE);
    construct_back(mButtons,BUTTON_ICONIFY);
    construct_back(mButtons,BUTTON_MAXIMIZE);

    CRect_i lRect=CRect_i(mClientXPos,mClientSize)+GetTotalExtent();

    PlaceButtons(lRect.Size());
    PlaceText();

    CreateDecoWindow(CPoint_d(lRect.l+mSL,lRect.t),CSize_d(lRect.w()-mSL-mSR,mT));
    CreateDecoWindow(CPoint_d(lRect.r-mSR-8,lRect.b-mSB-8),CSize_d(8,8));
    XDefineCursor(gui::x11::get_default_x11_display()->GetDisplay(),mDecoWindows[1],
                gui::CPointer::GetStock(gui::CPointer::STOCK_SIZENWSE)->GetID());
}

void CBarDecoration::InitStatic()
{
    CManagedDecoration::InitStatic();

    if(mImageInit) return;          //Already initialized

//    mShadow=gui::CGaussianMap::GetMap()->GetShadow(0.50,1500,1500,mShadowSize);
//    mBlackPicture=gui::CGaussianMap::GetMap()->GetSolid(true,1,0,0,0);

    mImageInit=true;
}

CRect_i CBarDecoration::GetTotalExtent()
{
    return CRect_i(-mL,-mT,mR,mB);
}

CRect_i CBarDecoration::GetFrameExtent()
{
    return CRect_i(-1,-mT,1,1);
}

#if 0
void CBarDecoration::CreatePixmap()
{
    if(!mPixmap)
    {
        mPixmap=new gui::x11::CPixmap;
        mPixmap->Create(CSize_i(mClientSize+GetExtraSize()),32);
    }

    CGC *lGC=mPixmap->GetGC(GC_VECTOR);

    CRect_d lRect(CPoint(0,0),mClientSize+GetExtraSize());

    CColor lBlack(0,0,0,0);
    lGC->SetSourceColor(lBlack);
    lGC->SetOperator(CGC::OP_SRC);
//  lGC->Rectangle(CPoint(mSL,mST),CSize(lRect.r-mSR-mSL,lRect.b-mSB-mST));
    lGC->Rectangle(lRect.TopLeft(),lRect.Size());
    lGC->FillPath();
    lGC->SetOperator(CGC::OP_OVER);

    //draw decoration
    gui::CTheme::EDecoSomthing lFlags=gui::CTheme::DECO_MAX|gui::CTheme::DECO_MIN|gui::CTheme::DECO_CLOSE;
    if(mInside)
        lFlags|=gui::CTheme::DECO_MOUSEIN;
    if(mActive)
        lFlags|=gui::CTheme::DECO_ACTIVE;

    gui::get_default_theme()->DrawDecoration(lGC,CRect(mSL,0,lRect.r-mSR,lRect.b-mSB),
                            mClient->GetVisibleName(),lFlags);

    //draw shadow
    CSize_d lSize=lRect.Size();
    CSize_d lSize2(round(lSize.x/2),round(lSize.y/2));
    lGC->SetOperator(CGC::OP_SRC);
    //LT

    lGC->Composite(CPoint(0,0),CSize(mSL,lSize2.y),mBlackPicture,
        CPoint(0,0),CSize(nUndefined),mShadow,CPoint(0,0));
    //LB
    lGC->Composite(CPoint(0,lSize2.y),CSize(mSL,lSize.y-lSize2.y),mBlackPicture,
        CPoint(0,0),CSize(nUndefined),mShadow,CPoint(0,mShadowSize.y-(lSize.y-lSize2.y)));
    //lBL
    lGC->Composite(CPoint(mSL,lSize.y-mSB),CSize(lSize2.x-mSL,mSB),mBlackPicture,
        CPoint(0,0),CSize(nUndefined),mShadow,CPoint(mSL,mShadowSize.y-mSB));
    //lBR
    lGC->Composite(CPoint(lSize2.x,lSize.y-mSB),CSize(lSize.x-lSize2.x-mSR,mSB),mBlackPicture,
        CPoint(0,0),CSize(nUndefined),mShadow,CPoint(mShadowSize.x-(lSize.x-lSize2.x),mShadowSize.y-mSB));
    //RT
    lGC->Composite(CPoint(lSize.x-mSR,0),CSize(mSR,lSize2.y),mBlackPicture,
        CPoint(0,0),CSize(nUndefined),mShadow,CPoint(mShadowSize.x-mSR,0));
    //RB
    lGC->Composite(CPoint(lSize.x-mSR,lSize2.y),CSize(mSR,lSize.y-lSize2.y),mBlackPicture,
        CPoint(0,0),CSize(nUndefined),mShadow,CPoint(mShadowSize.x-mSR,mShadowSize.y-(lSize.y-lSize2.y)));
    lGC->SetOperator(CGC::OP_OVER);

    mPixmapValid=true;
    delete lGC;

    //why this is here:
    // if we don't sync the server, the decoration (which is a pixmap bound with GLXTextureFromPixmap) gets
    // committed by opengl before it is completely drawn by X, which looks ugly
    // another solution seems to be to unbind and bind the pixmap again, but is is much slower
    // the solution would be to not use pixmaps at all, but use a framebuffer object instead
    XSync(gui::x11::get_default_x11_display()->GetDisplay(),False);
}
#endif

void CBarDecoration::CreatePixmap()
{
    if(!mPixmap)
    {
        mPixmap=new gui::x11::CPixmap;
        mPixmap->Create(mClientSize+GetTotalExtent().Size(),32);
    }

    img::CGC *lGC=mPixmap->GetGC(img::EGCCaps::NONE);

    CRect_d lRect(CPoint_d(0,0),CSize_i(mClientSize+GetTotalExtent().Size()));

    //clear background
    img::CColor lBlack(0,0,0,0);
    lGC->SetSource(lBlack);
    lGC->SetOperator(img::ECompOp::SRC);
    lGC->Path().Rect(lRect.TopLeft(),lRect.Size());
    lGC->FillPath();

    //draw top bar
    img::CLinearGradient lBasePattern(CPoint_d(0,6),CPoint_d(0,mT));
    if(mActive)
    {
        lBasePattern.AddStop(img::CColor(1.0,1.0,1.0,1.0),0.0);
        lBasePattern.AddStop(img::CColor(1.0,1.0,1.0,1.0),0.4);
        lBasePattern.AddStop(img::CColor(0.85,0.85,0.85,1.0),1.00);
    }
    else
    {
        lBasePattern.AddStop(img::CColor(0.77,0.77,0.77,1.0),0.00);
        lBasePattern.AddStop(img::CColor(0.7,0.7,0.7,1.0),1.00);
    }
    lGC->Path().MoveTo(mSL+0.5,mT);
    lGC->Path().LineTo(mSL+0.5,8);
    lGC->Path().Arc(CPoint_d(mSL+4,8),3.5,M_PI,M_PI*1.5);
    lGC->Path().LineTo(lRect.r-mSR-4,4.5);
    lGC->Path().Arc(CPoint_d(lRect.r-mSR-4,8),3.5,-M_PI*0.5,0);
    lGC->Path().LineTo(lRect.r-mSR-0.5,mT);
    lGC->Path().Close();

    lGC->SetSource(lBasePattern);
    lGC->FillPath();

    lGC->Path().MoveTo(mSL+0.5,lRect.b-mSB-0.5);
    lGC->Path().LineTo(mSL+0.5,8);
    lGC->Path().Arc(CPoint_d(mSL+4,8),3.5,M_PI,M_PI*1.5);
    lGC->Path().LineTo(lRect.r-mSR-4,4.5);
    lGC->Path().Arc(CPoint_d(lRect.r-mSR-4,8),3.5,-M_PI*0.5,0);
    lGC->Path().LineTo(lRect.r-mSR-0.5,lRect.b-mSB-0.5);
    lGC->Path().Close();
    lGC->Path().MoveTo(mSL+1.5,mT-0.5);
    lGC->Path().LineTo(lRect.r-mSR-0.5,mT-0.5);
    lGC->SetSource(img::CColor(0.35,0.35,0.35,1.0));
    lGC->SetLineWidth(1.0);
    lGC->StrokePath();

    lGC->SetOperator(img::ECompOp::OVER);
    lGC->Path().MoveTo(mSL+1.5,mT+0.5);
    lGC->Path().LineTo(lRect.r-mSR-0.5,mT+0.5);
    lGC->Path().MoveTo(mSL+1.5,lRect.b-mSB-0.5);
    lGC->Path().LineTo(mSL+1.5,8);
    lGC->SetSource(img::CColor(1.0,1.0,1.0,0.25));
    lGC->StrokePath();
    lGC->Path().Arc(CPoint_d(mSL+4,8),2.5,M_PI,M_PI*1.5);
    lGC->Path().LineTo(lRect.r-mSR-4,5.5);
    lGC->SetSource(img::CColor(1.0,1.0,1.0,0.35));
    lGC->StrokePath();
    
/*    
//    lGC->SetSourceColor(CColor(1.0,1.0,1.0,0.9));
    lGC->SetSourceColor(CColor(0.65,0.65,0.65,1.0));
    lGC->MoveTo(CPoint(mSL,4.5));
    lGC->LineTo(CPoint(lRect.r-mSR,4.5));
    lGC->StrokePath();
    lGC->SetSourceColor(CColor(1.0,1.0,1.0,1.0));
    lGC->MoveTo(CPoint(mSL,5.5));
    lGC->LineTo(CPoint(lRect.r-mSR,5.5));
    lGC->StrokePath();
    lGC->SetSourceColor(CColor(0.65,0.65,0.65,1.0));
    lGC->MoveTo(CPoint(mSL,mT-0.5));
    lGC->LineTo(CPoint(lRect.r-mSR,mT-0.5));
    lGC->StrokePath();

    
    
    
    PPattern lBasePattern=lGC->NewPattern(CPoint(0,6),CPoint(0,mT));
    lBasePattern->AddStop(CColor(0.97,0.97,0.97,1.0),0.00);
    lBasePattern->AddStop(CColor(0.9,0.9,0.9,1.0),1.00);

    lGC->SetSourcePattern(lBasePattern);
//  lGC->SetSourceColor(CColor(1,1,1,0.5));
    lGC->Rectangle(CPoint(mSL,6),CSize(lRect.r-mSR-mSL,mT-6-1));
    lGC->FillPath();
*/
    lGC->SetOperator(img::ECompOp::OVER);
    
    lGC->SetStockFont(L"smallbold");
    CPoint_d lPos(50,15);
    lGC->SetSource(img::CColor(1.0,1.0,1.0,0.4));
    lGC->DrawText(lPos+1,mManagedClient->GetVisibleName(),img::ETextPos::VCENTER);
    lGC->SetSource(img::CColor(0.2,0.2,0.2,1.0));
    lGC->DrawText(lPos,mManagedClient->GetVisibleName(),img::ETextPos::VCENTER);

    //draw shadow
    CSize_d lSize=lRect.Size();
    CSize_d lSize2(round(lSize.x/2),round(lSize.y/2));
    lGC->SetOperator(img::ECompOp::SRC);
    //LT
/*
    lGC->Composite(CPoint_d(0,0),CSize_d(mSL,lSize2.y),mBlackPicture,CPoint_d(0,0),nUndefined,mShadow,CPoint_d(0,0));
    //LB
    lGC->Composite(CPoint_d(0,lSize2.y),CSize_d(mSL,lSize.y-lSize2.y),mBlackPicture,
        CPoint_d(0,0),nUndefined,mShadow,CPoint_d(0,mShadowSize.y-(lSize.y-lSize2.y)));
    //lBL
    lGC->Composite(CPoint_d(mSL,lSize.y-mSB),CSize_d(lSize2.x-mSL,mSB),mBlackPicture,
        CPoint_d(0,0),nUndefined,mShadow,CPoint_d(mSL,mShadowSize.y-mSB));
    //lBR
    lGC->Composite(CPoint_d(lSize2.x,lSize.y-mSB),CSize_d(lSize.x-lSize2.x-mSR,mSB),mBlackPicture,
        CPoint_d(0,0),nUndefined,mShadow,CPoint_d(mShadowSize.x-(lSize.x-lSize2.x),mShadowSize.y-mSB));
    //RT
    lGC->Composite(CPoint_d(lSize.x-mSR,0),CSize_d(mSR,lSize2.y),mBlackPicture,
        CPoint_d(0,0),nUndefined,mShadow,CPoint_d(mShadowSize.x-mSR,0));
    //RB
    lGC->Composite(CPoint_d(lSize.x-mSR,lSize2.y),CSize_d(mSR,lSize.y-lSize2.y),mBlackPicture,
        CPoint_d(0,0),nUndefined,mShadow,CPoint_d(mShadowSize.x-mSR,mShadowSize.y-(lSize.y-lSize2.y)));
*/
    mPixmapValid=true;
    delete lGC;

    //why this is here:
    // if we don't sync the server, the decoration (which is a pixmap bound with GLXTextureFromPixmap) gets
    // committed by opengl before it is completely drawn by X, which looks ugly
    // another solution seems to be to unbind and bind the pixmap again, but is is much slower
    // the solution would be to not use pixmaps at all, but use a framebuffer object instead
    XSync(gui::x11::get_default_x11_display()->GetDisplay(),False);
}

void CBarDecoration::PlaceText()
{
    mTitleLayout.SetDefaultFont(img::CFont::GetStock(L"wmdeco"));
    mTitleLayout.SetText(L"dummy");
    mTitleLayout.SetWidth(mTitleWidth);
}

void CBarDecoration::PlaceButtons(const CSize_d &pSize)
{
    mButtons[0]->mRect={CPoint_d(pSize.x-31-mSR,1),CSize_d(20,18)};
    mButtons[1]->mRect={CPoint_d(pSize.x-51-mSR,1),CSize_d(20,18)};
    mButtons[2]->mRect={CPoint_d(pSize.x-71-mSR,1),CSize_d(20,18)};

    mTitleWidth=100;
    mTitleX=70;

    CRect_d lRect(CPoint_d(0,0),pSize);
}

void CBarDecoration::OnLabelChanged()
{
    PlaceText();
    Refresh();
}

void CBarDecoration::OnGeometryChanged(bool pResize,const CPoint_i &pPos,
                                    const CSize_i &pSize,const CPoint_i &pXPos)
{
    CManagedDecoration::OnGeometryChanged(pResize,pPos,pSize,pXPos);

    CRect_i lRect=CRect_i(mClientXPos,mClientSize)+GetTotalExtent();

    if(pResize)
    {
        PlaceButtons(lRect.Size());
        PlaceText();
    }

    XMoveResizeWindow(gui::x11::get_default_x11_display()->GetDisplay(),mDecoWindows[0],
                        (int)lRect.l+mSL,(int)lRect.t,(int)lRect.w()-mSL-mSR,mT);
                        XMoveResizeWindow(gui::x11::get_default_x11_display()->GetDisplay(),mDecoWindows[1],
                        (int)lRect.r-mSR-8,(int)lRect.b-mSB-8,8,8);
}

void CBarDecoration::OnButtonDown(const CPoint_d &pP,gui::EMouseButton pButton,int pMods)
{
    CManagedDecoration::OnButtonDown(pP,pButton,pMods);

    CPoint_d lPoint=pP-CPoint_d(mClientXPos);

    for(int i=0;i<mButtons.size();i++)
    {
        if(!mButtons[i]->mEnabled) continue;
        if(mButtons[i]->mRect.Contains(lPoint))
        {
            mPressed=mButtons[i].get();
            mInside=mButtons[i].get();
//          RefreshRect(mButtons[i]->mRect);
            Refresh();
            return;
        }
    }
}

void CBarDecoration::OnButtonUp(const CPoint_d &pP,gui::EMouseButton pButton,int pMods)
{
    if(mPressed)
    {
        CBarButton *lPressed=mPressed;
        mPressed=nullptr;
        if(mInside==lPressed)
        {
//          RefreshRect(lPressed->mRect);
            Refresh();

            if(lPressed->mID==BUTTON_MENU)
                eShowMenu(pP);
            else
                eButton(lPressed->mID);
        }

    }

    CManagedDecoration::OnButtonUp(pP,pButton,pMods);
}

void CBarDecoration::OnMouseMove(const CPoint_d &pP)
{
    CBarButton *lInside=nullptr;

    CPoint_d lPoint=pP-CPoint_d(mClientXPos);

    for(int i=0;i<mButtons.size();i++)
    {
        if(mButtons[i]->mRect.Contains(lPoint))
        {
            lInside=mButtons[i].get();
            break;
        }
    }

    if(mInside!=lInside)
    {
//      CBarButton *lOld=mInside;
        mInside=lInside;
//      if(lOld)
//          RefreshRect(lOld->mRect);
//      if(mInside)
//          RefreshRect(mInside->mRect);
        Refresh();
    }

    if(mPressed) return;

    CManagedDecoration::OnMouseMove(pP);
}

void CBarDecoration::OnMouseOut()
{
    if(mInside)
    {
//      CBarButton *lOld=mInside;
        mInside=nullptr;
        Refresh();
//      RefreshRect(lOld->mRect);
    }
}

gui::EDragState CBarDecoration::GetDraggingZone(const CPoint_d &pPoint)
{
    CRect_d lTitleBar(mSL,0,mClientSize.x-mSR,mT);

    CRect_d lRect(CPoint_d(0,0),mClientSize+GetTotalExtent().Size());
    
    for(int i=0;i<mButtons.size();i++)
    {
        if(!mButtons[i]->mEnabled) continue;
        if(mButtons[i]->mRect.Contains(pPoint))
            return gui::DRAG_NONE;
    }

    if(lTitleBar.Contains(pPoint))
        return gui::DRAG_MOVE;
    else
    {
        gui::EDragState lDraggingZone=gui::DRAG_NONE;

        if(pPoint.x<mL+15&&pPoint.x<lRect.r/2)
            lDraggingZone|=gui::DRAG_WEST;
        if(pPoint.y<mT+15&&pPoint.y<lRect.b/2)
            lDraggingZone|=gui::DRAG_NORTH;
        if(pPoint.x>lRect.r-mR-15&&pPoint.x>lRect.r/2)
            lDraggingZone|=gui::DRAG_EAST;
        if(pPoint.y>lRect.b-mB-15&&pPoint.y>lRect.b/2)
            lDraggingZone|=gui::DRAG_SOUTH;

        return lDraggingZone;
    }
}

/*namespace bwm*/ } /*namespace buola*/ }
